{
 "cells": [
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "# Zigent实现出题智能体\n",
    "\n",
    "本节课我们将通过Zigent框架实现一个出题智能体，其主要功能是根据指定的Markdown文件内容自动生成考卷。该智能体支持单选题、多选题和填空题三种题型，并能将生成的考卷保存为Markdown文件。\n",
    "\n",
    "## 设计思路\n",
    "\n",
    "出题智能体的核心功能包括：\n",
    "\n",
    "1. 从指定目录加载Markdown文件作为知识来源\n",
    "2. 根据用户指定的受众群体和考察目的生成考卷\n",
    "3. 支持多种题型（单选题、多选题、填空题）\n",
    "4. 自动保存生成的考卷并返回结果路径\n",
    "\n",
    "## 代码实现\n",
    "\n",
    "### 1. 引入依赖\n",
    "\n",
    "首先引入必要的依赖："
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 1,
   "metadata": {},
   "outputs": [],
   "source": [
    "import os\n",
    "from pathlib import Path\n",
    "from datetime import datetime\n",
    "from typing import Dict, List\n",
    "import json\n",
    "\n",
    "from zigent.llm.agent_llms import LLM\n",
    "from zigent.actions import BaseAction, ThinkAct, FinishAct\n",
    "from zigent.agents import BaseAgent\n",
    "from zigent.commons import TaskPackage, AgentAct\n",
    "from zigent.actions.InnerActions import INNER_ACT_KEY"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "\n",
    "### 2. 定义出题Action\n",
    "\n",
    "定义`QuizGenerationAction`类，负责生成考卷题目："
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 2,
   "metadata": {},
   "outputs": [],
   "source": [
    "class QuizGenerationAction(BaseAction):\n",
    "    \"\"\"Generate quiz questions from markdown content\"\"\"\n",
    "    def __init__(self, llm: LLM) -> None:\n",
    "        action_name = \"GenerateQuiz\"\n",
    "        action_desc = \"Generate quiz questions from markdown content\"\n",
    "        params_doc = {\n",
    "            \"content\": \"(Type: string): The markdown content to generate questions from\",\n",
    "            \"question_types\": \"(Type: list): List of question types to generate\",\n",
    "            \"audience\": \"(Type: string): Target audience for the quiz\",\n",
    "            \"purpose\": \"(Type: string): Purpose of the quiz\"\n",
    "        }\n",
    "        super().__init__(action_name, action_desc, params_doc)\n",
    "        self.llm = llm\n",
    "        \n",
    "    def __call__(self, **kwargs):\n",
    "        content = kwargs.get(\"content\", \"\")\n",
    "        question_types = kwargs.get(\"question_types\", [])\n",
    "        audience = kwargs.get(\"audience\", \"\")\n",
    "        purpose = kwargs.get(\"purpose\", \"\")\n",
    "        \n",
    "        prompt = f\"\"\"\n",
    "        你是一个辅助设计考卷的机器人,全程使用中文。\n",
    "        你的任务是帮助用户快速创建、设计考卷，考卷以markdown格式给出。\n",
    "        \n",
    "        要求：\n",
    "        1. 受众群体：{audience}\n",
    "        2. 考察目的：{purpose}\n",
    "        3. 需要包含以下题型：{\", \".join(question_types)}\n",
    "        4. 考卷格式要求：\n",
    "        \"\"\"\n",
    "        prompt += \"\"\"\n",
    "        # 问卷标题\n",
    "        ---\n",
    "        1. 这是判断题的题干?\n",
    "            - (x) True\n",
    "            - ( ) False\n",
    "        # (x)为正确答案\n",
    "\n",
    "        2. 这是单选题的题干\n",
    "            - (x) 这是正确选项\n",
    "            - ( ) 这是错误选项\n",
    "        # (x)为正确答案\n",
    "\n",
    "        3. 这是多选题的题干?\n",
    "            - [x] 这是正确选项1\n",
    "            - [x] 这是正确选项2\n",
    "            - [ ] 这是错误选项1\n",
    "            - [ ] 这是错误选项2\n",
    "        # [x]为正确答案\n",
    "\n",
    "        4. 这是填空题的题干?\n",
    "            - R:= 填空题答案\n",
    "        #填空题正确答案格式\n",
    "        \"\"\"\n",
    "        \n",
    "        prompt += f\"\\n请根据以下内容生成考卷：\\n{content}\"\n",
    "        \n",
    "        quiz_content = self.llm.run(prompt)\n",
    "        return {\n",
    "            \"quiz_content\": quiz_content,\n",
    "            \"audience\": audience,\n",
    "            \"purpose\": purpose,\n",
    "            \"question_types\": question_types\n",
    "        }"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "### 3. 定义保存Action\n",
    "\n",
    "定义`SaveQuizAction`类，负责保存生成的考卷："
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 3,
   "metadata": {},
   "outputs": [],
   "source": [
    "class SaveQuizAction(BaseAction):\n",
    "    \"\"\"Save quiz to file and return URL\"\"\"\n",
    "    def __init__(self) -> None:\n",
    "        action_name = \"SaveQuiz\"\n",
    "        action_desc = \"Save quiz content to file and return URL\"\n",
    "        params_doc = {\n",
    "            \"quiz_content\": \"(Type: string): The quiz content to save\",\n",
    "            \"quiz_title\": \"(Type: string): Title of the quiz\"\n",
    "        }\n",
    "        super().__init__(action_name, action_desc, params_doc)\n",
    "        \n",
    "    def __call__(self, **kwargs):\n",
    "        quiz_content = kwargs.get(\"quiz_content\", \"\")\n",
    "        quiz_title = kwargs.get(\"quiz_title\", \"quiz\")\n",
    "        \n",
    "        output_dir = datetime.now().strftime(\"%Y-%m-%d_%H-%M-%S\")\n",
    "        os.makedirs(output_dir, exist_ok=True)\n",
    "        \n",
    "        output_file = os.path.join(output_dir, f\"{quiz_title}.md\")\n",
    "        with open(output_file, 'w', encoding='utf-8') as f:\n",
    "            f.write(quiz_content)\n",
    "            \n",
    "        return {\n",
    "            \"file_path\": output_file,\n",
    "            \"quiz_url\": f\"/{output_file}\"\n",
    "        }"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "\n",
    "### 4. 定义出题智能体\n",
    "\n",
    "定义`QuizGeneratorAgent`类，管理整个出题流程："
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 5,
   "metadata": {},
   "outputs": [],
   "source": [
    "class QuizGeneratorAgent(BaseAgent):\n",
    "    \"\"\"Quiz generation agent that manages quiz creation process\"\"\"\n",
    "    def __init__(\n",
    "        self,\n",
    "        llm: LLM,\n",
    "        markdown_dir: str\n",
    "    ):\n",
    "        name = \"QuizGeneratorAgent\"\n",
    "        role = \"\"\"你是一个专业的考卷生成助手。你可以根据提供的Markdown内容生成结构良好、\n",
    "        内容全面的考卷。你擅长根据受众群体和考察目的设计合适的题目。\"\"\"\n",
    "        \n",
    "        super().__init__(\n",
    "            name=name,\n",
    "            role=role,\n",
    "            llm=llm,\n",
    "        )\n",
    "        \n",
    "        self.markdown_dir = markdown_dir\n",
    "        self.quiz_action = QuizGenerationAction(llm)\n",
    "        self.save_action = SaveQuizAction()\n",
    "        \n",
    "        self._add_quiz_example()\n",
    "        \n",
    "    def _load_markdown_content(self) -> str:\n",
    "        \"\"\"Load all markdown files from directory\"\"\"\n",
    "        content = []\n",
    "        for root, _, files in os.walk(self.markdown_dir):\n",
    "            for file in files:\n",
    "                if file.endswith(\".md\"):\n",
    "                    with open(os.path.join(root, file), 'r', encoding='utf-8') as f:\n",
    "                        content.append(f.read())\n",
    "        return \"\\n\".join(content)\n",
    "        \n",
    "    def __call__(self, task: TaskPackage):\n",
    "        \"\"\"Process the quiz generation task\"\"\"\n",
    "        # Parse task parameters\n",
    "        params = json.loads(task.instruction)\n",
    "        audience = params.get(\"audience\", \"\")\n",
    "        purpose = params.get(\"purpose\", \"\")\n",
    "        question_types = params.get(\"question_types\", [])\n",
    "        \n",
    "        # Load markdown content\n",
    "        content = self._load_markdown_content()\n",
    "        \n",
    "        # Generate quiz\n",
    "        quiz_result = self.quiz_action(\n",
    "            content=content,\n",
    "            question_types=question_types,\n",
    "            audience=audience,\n",
    "            purpose=purpose\n",
    "        )\n",
    "        \n",
    "        # Save quiz\n",
    "        save_result = self.save_action(\n",
    "            quiz_content=quiz_result[\"quiz_content\"],\n",
    "            quiz_title=\"generated_quiz\"\n",
    "        )\n",
    "        \n",
    "        task.answer = {\n",
    "            \"quiz_content\": quiz_result[\"quiz_content\"],\n",
    "            \"quiz_url\": save_result[\"quiz_url\"]\n",
    "        }\n",
    "        task.completion = \"completed\"\n",
    "        \n",
    "        return task\n",
    "\n",
    "\n",
    "    def _add_quiz_example(self):\n",
    "        \"\"\"Add an illustration example for the quiz generator\"\"\"\n",
    "        exp_task = json.dumps({\n",
    "            \"audience\": \"零基础\",  # 受众\n",
    "            \"purpose\": \"测试Python基础知识掌握情况\", # 目的\n",
    "            \"question_types\": [\"单选题\", \"多选题\", \"填空题\"] # 题型\n",
    "        })\n",
    "        exp_task_pack = TaskPackage(instruction=exp_task)\n",
    "        \n",
    "        act_1 = AgentAct(\n",
    "            name=ThinkAct.action_name,\n",
    "            params={INNER_ACT_KEY: \"\"\"首先，我会加载Markdown内容，然后根据受众群体和考察目的生成考卷。\"\"\"}\n",
    "        )\n",
    "        obs_1 = \"OK. 开始加载Markdown内容。\"\n",
    "        \n",
    "        act_2 = AgentAct(\n",
    "            name=self.quiz_action.action_name,\n",
    "            params={\n",
    "                \"content\": \"Python基础内容...\",\n",
    "                \"question_types\": [\"单选题\", \"多选题\", \"填空题\"],\n",
    "                \"audience\": \"零基础\",\n",
    "                \"purpose\": \"测试Python基础知识掌握情况\"\n",
    "            }\n",
    "        )\n",
    "        obs_2 = \"\"\"# Python基础测试\n",
    "        1. Python是什么类型的语言?\n",
    "            - (x) 解释型\n",
    "            - ( ) 编译型\n",
    "        # (x)为正确答案\"\"\"\n",
    "        \n",
    "        act_3 = AgentAct(\n",
    "            name=self.save_action.action_name,\n",
    "            params={\n",
    "                \"quiz_content\": obs_2,\n",
    "                \"quiz_title\": \"Python基础测试\"\n",
    "            }\n",
    "        )\n",
    "        obs_3 = {\"file_path\": \"2025-01-15_03-37-40/Python基础测试.md\", \"quiz_url\": \"/2025-01-15_03-37-40/Python基础测试.md\"}\n",
    "        \n",
    "        act_4 = AgentAct(\n",
    "            name=FinishAct.action_name,\n",
    "            params={INNER_ACT_KEY: \"考卷生成并保存成功。\"}\n",
    "        )\n",
    "        obs_4 = \"考卷生成任务完成。\"\n",
    "        \n",
    "        exp_act_obs = [(act_1, obs_1), (act_2, obs_2), (act_3, obs_3), (act_4, obs_4)]\n",
    "        \n",
    "        self.prompt_gen.add_example(\n",
    "            task=exp_task_pack,\n",
    "            action_chain=exp_act_obs\n",
    "        )"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "## 使用示例\n",
    "\n",
    "可以通过以下方式使用出题智能体，记得修改指定包含Markdown文件的目录并定义考卷参数 ："
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "生成的考卷内容：\n",
      "# 零基础基础知识测试考卷\n",
      "---\n",
      "1. 计算机的基本组成部分包括以下哪些？（多选题）\n",
      "    - [x] 中央处理器\n",
      "    - [x] 内存\n",
      "    - [ ] 打印机\n",
      "    - [ ] 显示器\n",
      "    - [x] 硬盘\n",
      "    - [ ] 键盘\n",
      "# [x]为正确答案\n",
      "\n",
      "2. 以下哪项是操作系统？（单选题）\n",
      "    - (x) Windows\n",
      "    - ( ) Word\n",
      "    - ( ) Excel\n",
      "    - ( ) Photoshop\n",
      "# (x)为正确答案\n",
      "\n",
      "3. 互联网的基本概念是什么？（填空题）\n",
      "    - R:= 全球计算机网络\n",
      "#填空题正确答案格式\n",
      "\n",
      "4. 以下哪项是编程语言？（单选题）\n",
      "    - (x) Python\n",
      "    - ( ) HTML\n",
      "    - ( ) CSS\n",
      "    - ( ) JPEG\n",
      "# (x)为正确答案\n",
      "\n",
      "5. 以下哪项是数据库管理系统？（单选题）\n",
      "    - (x) MySQL\n",
      "    - ( ) Word\n",
      "    - ( ) Excel\n",
      "    - ( ) Photoshop\n",
      "# (x)为正确答案\n",
      "\n",
      "6. 以下哪项是图像文件格式？（单选题）\n",
      "    - ( ) MySQL\n",
      "    - ( ) Word\n",
      "    - ( ) Excel\n",
      "    - (x) JPEG\n",
      "# (x)为正确答案\n",
      "\n",
      "7. 以下哪项是文本文件格式？（单选题）\n",
      "    - ( ) MySQL\n",
      "    - (x) TXT\n",
      "    - ( ) Excel\n",
      "    - ( ) JPEG\n",
      "# (x)为正确答案\n",
      "\n",
      "8. 以下哪项是视频文件格式？（单选题）\n",
      "    - ( ) MySQL\n",
      "    - ( ) Word\n",
      "    - ( ) Excel\n",
      "    - (x) MP4\n",
      "# (x)为正确答案\n",
      "\n",
      "9. 以下哪项是音频文件格式？（单选题）\n",
      "    - ( ) MySQL\n",
      "    - ( ) Word\n",
      "    - ( ) Excel\n",
      "    - (x) MP3\n",
      "# (x)为正确答案\n",
      "\n",
      "10. 以下哪项是文档文件格式？（单选题）\n",
      "    - ( ) MySQL\n",
      "    - (x) PDF\n",
      "    - ( ) Excel\n",
      "    - ( ) JPEG\n",
      "# (x)为正确答案\n",
      "考卷路径: /2025-02-25_02-11-39\\generated_quiz.md\n"
     ]
    }
   ],
   "source": [
    "from dotenv import load_dotenv\n",
    "load_dotenv()\n",
    "\n",
    "api_key = os.getenv('ZISHU_API_KEY')\n",
    "base_url = \"http://101.132.164.17:8000/v1\"\n",
    "chat_model = \"DeepSeek-V3\"\n",
    "\n",
    "llm = LLM(api_key=api_key, base_url=base_url, model_name=chat_model)\n",
    "\n",
    "# 创建出题智能体\n",
    "markdown_dir = \"../docs\"  # 指定包含Markdown文件的目录\n",
    "agent = QuizGeneratorAgent(llm=llm, markdown_dir=markdown_dir)\n",
    "\n",
    "# 定义考卷参数\n",
    "quiz_params = {\n",
    "    \"audience\": \"零基础\", # 受众群体\n",
    "    \"purpose\": \"测试基础知识掌握情况\", # 考察目的\n",
    "    \"question_types\": [\"单选题\"] # 需要包含的题型\n",
    "}\n",
    "\n",
    "# 生成考卷\n",
    "task = TaskPackage(instruction=json.dumps(quiz_params))\n",
    "result = agent(task)\n",
    "\n",
    "print(\"生成的考卷内容：\")\n",
    "print(result.answer[\"quiz_content\"])\n",
    "print(f\"考卷路径: {result.answer['quiz_url']}\")"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "理想情况下，输出类似：\n",
    "```\n",
    "生成的考卷内容：\n",
    "# AI Agent基础知识测试卷\n",
    "---\n",
    "\n",
    "1. wow-agent是自塾（zishu.co）出品的第几个开源项目？\n",
    "    - ( ) 第一个\n",
    "    - (x) 第三个\n",
    "    - ( ) 第五个\n",
    "    - ( ) 第七个\n",
    "# (x)为正确答案\n",
    "\n",
    "2. 以下哪个不是自塾在2024年出品的开源项目？\n",
    "    - ( ) wow-fullstack\n",
    "    - ( ) wow-rag\n",
    "    - (x) wow-llm\n",
    "    - ( ) wow-agent\n",
    "# (x)为正确答案\n",
    "\n",
    "3. 以下哪些是Agents的核心组件？（多选）\n",
    "    - [x] 模型 (Model)\n",
    "    - [x] 工具 (Tools)\n",
    "    - [x] 编排层 (Orchestration Layer)\n",
    "    - [ ] 数据库 (Database)\n",
    "# [x]为正确答案\n",
    "\n",
    "4. Agents的运作机制中，以下哪个步骤是正确的？\n",
    "    - ( ) 接收输入 -> 执行行动 -> 理解输入 -> 推理规划\n",
    "    - (x) 接收输入 -> 理解输入 -> 推理规划 -> 执行行动\n",
    "    - ( ) 理解输入 -> 接收输入 -> 推理规划 -> 执行行动\n",
    "    - ( ) 推理规划 -> 接收输入 -> 理解输入 -> 执行行动\n",
    "# (x)为正确答案\n",
    "\n",
    "5. 以下哪个不是Agents的优势？\n",
    "    - ( ) 知识扩展\n",
    "    - ( ) 自主行动\n",
    "    - ( ) 多轮交互\n",
    "    - (x) 依赖大量人工干预\n",
    "# (x)为正确答案\n",
    "\n",
    "6. 以下哪个是Agents的应用场景？\n",
    "    - ( ) 智能客服\n",
    "    - ( ) 个性化推荐\n",
    "    - ( ) 虚拟助手\n",
    "    - (x) 以上都是\n",
    "# (x)为正确答案\n",
    "\n",
    "7. 以下哪个是Agents的开发工具？\n",
    "    - ( ) LangChain\n",
    "    - ( ) LangGraph\n",
    "    - ( ) Vertex AI\n",
    "    - (x) 以上都是\n",
    "# (x)为正确答案\n",
    "\n",
    "8. 以下哪个是结构化Prompt的标识符？\n",
    "    - ( ) #\n",
    "    - ( ) <>\n",
    "    - ( ) -\n",
    "    - (x) 以上都是\n",
    "# (x)为正确答案\n",
    "\n",
    "9. 以下哪个是结构化Prompt的属性词？\n",
    "    - ( ) Role\n",
    "    - ( ) Profile\n",
    "    - ( ) Initialization\n",
    "    - (x) 以上都是\n",
    "# (x)为正确答案\n",
    "\n",
    "10. 以下哪个是LangGPT的Role模板中的内容？\n",
    "    - ( ) Role\n",
    "    - ( ) Profile\n",
    "    - ( ) Rules\n",
    "    - (x) 以上都是\n",
    "# (x)为正确答案\n",
    "\n",
    "11. 以下哪个是prompt设计方法论的步骤？\n",
    "    - ( ) 数据准备\n",
    "    - ( ) 模型选择\n",
    "    - ( ) 提示词设计\n",
    "    - (x) 以上都是\n",
    "# (x)为正确答案\n",
    "\n",
    "12. 以下哪个是智能客服智能体的业务线？\n",
    "    - ( ) 用户注册\n",
    "    - ( ) 用户数据查询\n",
    "    - ( ) 删除用户数据\n",
    "    - (x) 以上都是\n",
    "# (x)为正确答案\n",
    "\n",
    "13. 以下哪个是智能客服智能体的注册任务中需要获取的用户信息？\n",
    "    - ( ) 用户名、性别、年龄\n",
    "    - ( ) 用户设置的密码\n",
    "    - ( ) 用户的电子邮件地址\n",
    "    - (x) 以上都是\n",
    "# (x)为正确答案\n",
    "\n",
    "14. 以下哪个是智能客服智能体的查询任务中需要获取的用户信息？\n",
    "    - ( ) 用户ID\n",
    "    - ( ) 用户设置的密码\n",
    "    - (x) 以上都是\n",
    "    - ( ) 用户的电子邮件地址\n",
    "# (x)为正确答案\n",
    "\n",
    "15. 以下哪个是智能客服智能体的删除任务中需要获取的用户信息？\n",
    "    - ( ) 用户ID\n",
    "    - ( ) 用户设置的密码\n",
    "    - ( ) 用户的电子邮件地址\n",
    "    - (x) 以上都是\n",
    "# (x)为正确答案\n",
    "\n",
    "16. 以下哪个是阅卷智能体的输出格式？\n",
    "    - ( ) JSON\n",
    "    - ( ) XML\n",
    "    - ( ) YAML\n",
    "    - (x) 以上都是\n",
    "# (x)为正确答案\n",
    "\n",
    "17. 以下哪个是阅卷智能体的评分标准？\n",
    "    - ( ) 宽松\n",
    "    - ( ) 严格\n",
    "    - (x) 适当宽松\n",
    "    - ( ) 以上都是\n",
    "# (x)为正确答案\n",
    "\n",
    "18. 以下哪个是阅卷智能体的评分范围？\n",
    "    - ( ) 0-5分\n",
    "    - (x) 0-10分\n",
    "    - ( ) 0-100分\n",
    "    - ( ) 以上都是\n",
    "# (x)为正确答案\n",
    "\n",
    "19. 以下哪个是阅卷智能体的评分结果？\n",
    "    - ( ) llmgetscore\n",
    "    - ( ) llmcomments\n",
    "    - (x) 以上都是\n",
    "    - ( ) 以上都不是\n",
    "# (x)为正确答案\n",
    "\n",
    "20. 以下哪个是阅卷智能体的评分结果中的评语？\n",
    "    - ( ) llmgetscore\n",
    "    - (x) llmcomments\n",
    "    - ( ) 以上都是\n",
    "    - ( ) 以上都不是\n",
    "# (x)为正确答案\n",
    "考卷URL: /2025-01-15_12-46-31\\generated_quiz.md\n",
    "```"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "## 总结\n",
    "\n",
    "总的来说，我们通过Zigent框架实现的简单的出题智能体，实现了我们一开始的设计：\n",
    "\n",
    "1. 根据指定目录的Markdown内容自动生成考卷\n",
    "2. 支持多种题型\n",
    "3. 自动保存生成的考卷\n",
    "4. 提供简单的命令行交互界面\n",
    "\n",
    "未来我们可以进一步扩展功能，如支持更多题型、自动阅卷评分等功能。"
   ]
  }
 ],
 "metadata": {
  "kernelspec": {
   "display_name": "dbgpt_env",
   "language": "python",
   "name": "python3"
  },
  "language_info": {
   "codemirror_mode": {
    "name": "ipython",
    "version": 3
   },
   "file_extension": ".py",
   "mimetype": "text/x-python",
   "name": "python",
   "nbconvert_exporter": "python",
   "pygments_lexer": "ipython3",
   "version": "3.10.16"
  }
 },
 "nbformat": 4,
 "nbformat_minor": 2
}
